1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 package sun.font;
27
28 import java.nio.ByteBuffer;
29 import java.nio.CharBuffer;
30 import java.nio.IntBuffer;
31 import java.util.Locale;
32 import java.nio.charset.*;
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 abstract class CMap {
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124 static final short ShiftJISEncoding = 2;
125 static final short GBKEncoding = 3;
126 static final short Big5Encoding = 4;
127 static final short WansungEncoding = 5;
128 static final short JohabEncoding = 6;
129 static final short MSUnicodeSurrogateEncoding = 10;
130
131 static final char noSuchChar = (char)0xfffd;
132 static final int SHORTMASK = 0x0000ffff;
133 static final int INTMASK = 0xffffffff;
134
135 static final char[][] converterMaps = new char[7][];
136
137
138
139
140
141
142 char[] xlat;
143
144 static CMap initialize(TrueTypeFont font) {
145
146 CMap cmap = null;
147
148 int offset, platformID, encodingID=-1;
149
150 int three0=0, three1=0, three2=0, three3=0, three4=0, three5=0,
151 three6=0, three10=0;
152 boolean threeStar = false;
153
154 ByteBuffer cmapBuffer = font.getTableBuffer(TrueTypeFont.cmapTag);
155 int cmapTableOffset = font.getTableSize(TrueTypeFont.cmapTag);
156 short numberSubTables = cmapBuffer.getShort(2);
157
158
159 for (int i=0; i<numberSubTables; i++) {
160 cmapBuffer.position(i * 8 + 4);
161 platformID = cmapBuffer.getShort();
162 if (platformID == 3) {
163 threeStar = true;
164 encodingID = cmapBuffer.getShort();
165 offset = cmapBuffer.getInt();
166 switch (encodingID) {
167 case 0: three0 = offset; break;
168 case 1: three1 = offset; break;
169 case 2: three2 = offset; break;
170 case 3: three3 = offset; break;
171 case 4: three4 = offset; break;
172 case 5: three5 = offset; break;
173 case 6: three6 = offset; break;
174 case 10: three10 = offset; break;
175 }
176 }
177 }
178
179
180 if (threeStar) {
181 if (three10 != 0) {
182 cmap = createCMap(cmapBuffer, three10, null);
183 }
184 else if (three0 != 0) {
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214 cmap = createCMap(cmapBuffer, three0, null);
215
216 }
217 else if (three1 != 0) {
218 cmap = createCMap(cmapBuffer, three1, null);
219 }
220 else if (three2 != 0) {
221 cmap = createCMap(cmapBuffer, three2,
222 getConverterMap(ShiftJISEncoding));
223 }
224 else if (three3 != 0) {
225 cmap = createCMap(cmapBuffer, three3,
226 getConverterMap(GBKEncoding));
227 }
228 else if (three4 != 0) {
229
230
231
232
233
234
235 if (FontUtilities.isSolaris && font.platName != null &&
236 (font.platName.startsWith(
237 "/usr/openwin/lib/locale/zh_CN.EUC/X11/fonts/TrueType") ||
238 font.platName.startsWith(
239 "/usr/openwin/lib/locale/zh_CN/X11/fonts/TrueType") ||
240 font.platName.startsWith(
241 "/usr/openwin/lib/locale/zh/X11/fonts/TrueType"))) {
242 cmap = createCMap(cmapBuffer, three4,
243 getConverterMap(GBKEncoding));
244 }
245 else {
246 cmap = createCMap(cmapBuffer, three4,
247 getConverterMap(Big5Encoding));
248 }
249 }
250 else if (three5 != 0) {
251 cmap = createCMap(cmapBuffer, three5,
252 getConverterMap(WansungEncoding));
253 }
254 else if (three6 != 0) {
255 cmap = createCMap(cmapBuffer, three6,
256 getConverterMap(JohabEncoding));
257 }
258 } else {
259
260
261
262
263 cmap = createCMap(cmapBuffer, cmapBuffer.getInt(8), null);
264 }
265 return cmap;
266 }
267
268
269
270
271 static char[] getConverter(short encodingID) {
272 int dBegin = 0x8000;
273 int dEnd = 0xffff;
274 String encoding;
275
276 switch (encodingID) {
277 case ShiftJISEncoding:
278 dBegin = 0x8140;
279 dEnd = 0xfcfc;
280 encoding = "SJIS";
281 break;
282 case GBKEncoding:
283 dBegin = 0x8140;
284 dEnd = 0xfea0;
285 encoding = "GBK";
286 break;
287 case Big5Encoding:
288 dBegin = 0xa140;
289 dEnd = 0xfefe;
290 encoding = "Big5";
291 break;
292 case WansungEncoding:
293 dBegin = 0xa1a1;
294 dEnd = 0xfede;
295 encoding = "EUC_KR";
296 break;
297 case JohabEncoding:
298 dBegin = 0x8141;
299 dEnd = 0xfdfe;
300 encoding = "Johab";
301 break;
302 default:
303 return null;
304 }
305
306 try {
307 char[] convertedChars = new char[65536];
308 for (int i=0; i<65536; i++) {
309 convertedChars[i] = noSuchChar;
310 }
311
312 byte[] inputBytes = new byte[(dEnd-dBegin+1)*2];
313 char[] outputChars = new char[(dEnd-dBegin+1)];
314
315 int j = 0;
316 int firstByte;
317 if (encodingID == ShiftJISEncoding) {
318 for (int i = dBegin; i <= dEnd; i++) {
319 firstByte = (i >> 8 & 0xff);
320 if (firstByte >= 0xa1 && firstByte <= 0xdf) {
321
322 inputBytes[j++] = (byte)0xff;
323 inputBytes[j++] = (byte)0xff;
324 } else {
325 inputBytes[j++] = (byte)firstByte;
326 inputBytes[j++] = (byte)(i & 0xff);
327 }
328 }
329 } else {
330 for (int i = dBegin; i <= dEnd; i++) {
331 inputBytes[j++] = (byte)(i>>8 & 0xff);
332 inputBytes[j++] = (byte)(i & 0xff);
333 }
334 }
335
336 Charset.forName(encoding).newDecoder()
337 .onMalformedInput(CodingErrorAction.REPLACE)
338 .onUnmappableCharacter(CodingErrorAction.REPLACE)
339 .replaceWith("\u0000")
340 .decode(ByteBuffer.wrap(inputBytes, 0, inputBytes.length),
341 CharBuffer.wrap(outputChars, 0, outputChars.length),
342 true);
343
344
345 for (int i = 0x20; i <= 0x7e; i++) {
346 convertedChars[i] = (char)i;
347 }
348
349
350 if (encodingID == ShiftJISEncoding) {
351 for (int i = 0xa1; i <= 0xdf; i++) {
352 convertedChars[i] = (char)(i - 0xa1 + 0xff61);
353 }
354 }
355
356
357
358
359
360
361
362
363
364 System.arraycopy(outputChars, 0, convertedChars, dBegin,
365 outputChars.length);
366
367
368
369
370
371 char [] invertedChars = new char[65536];
372 for (int i=0;i<65536;i++) {
373 if (convertedChars[i] != noSuchChar) {
374 invertedChars[convertedChars[i]] = (char)i;
375 }
376 }
377 return invertedChars;
378
379 } catch (Exception e) {
380 e.printStackTrace();
381 }
382 return null;
383 }
384
385
386
387
388
389
390 static char[] getConverterMap(short encodingID) {
391 if (converterMaps[encodingID] == null) {
392 converterMaps[encodingID] = getConverter(encodingID);
393 }
394 return converterMaps[encodingID];
395 }
396
397
398 static CMap createCMap(ByteBuffer buffer, int offset, char[] xlat) {
399
400
401
402 int subtableFormat = buffer.getChar(offset);
403 long subtableLength;
404 if (subtableFormat < 8) {
405 subtableLength = buffer.getChar(offset+2);
406 } else {
407 subtableLength = buffer.getInt(offset+4) & INTMASK;
408 }
409 if (offset+subtableLength > buffer.capacity()) {
410 if (FontUtilities.isLogging()) {
411 FontUtilities.getLogger().warning("Cmap subtable overflows buffer.");
412 }
413 }
414 switch (subtableFormat) {
415 case 0: return new CMapFormat0(buffer, offset);
416 case 2: return new CMapFormat2(buffer, offset, xlat);
417 case 4: return new CMapFormat4(buffer, offset, xlat);
418 case 6: return new CMapFormat6(buffer, offset, xlat);
419 case 8: return new CMapFormat8(buffer, offset, xlat);
420 case 10: return new CMapFormat10(buffer, offset, xlat);
421 case 12: return new CMapFormat12(buffer, offset, xlat);
422 default: throw new RuntimeException("Cmap format unimplemented: " +
423 (int)buffer.getChar(offset));
424 }
425 }
426
427
428
429
430
431
432
433
434
435
436 abstract char getGlyph(int charCode);
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453 static class CMapFormat4 extends CMap {
454 int segCount;
455 int entrySelector;
456 int rangeShift;
457 char[] endCount;
458 char[] startCount;
459 short[] idDelta;
460 char[] idRangeOffset;
461 char[] glyphIds;
462
463 CMapFormat4(ByteBuffer bbuffer, int offset, char[] xlat) {
464
465 this.xlat = xlat;
466
467 bbuffer.position(offset);
468 CharBuffer buffer = bbuffer.asCharBuffer();
469 buffer.get();
470 int subtableLength = buffer.get();
471
472
473
474
475
476
477
478
479 if (offset+subtableLength > bbuffer.capacity()) {
480 subtableLength = bbuffer.capacity() - offset;
481 }
482 buffer.get();
483 segCount = buffer.get()/2;
484 int searchRange = buffer.get();
485 entrySelector = buffer.get();
486 rangeShift = buffer.get()/2;
487 startCount = new char[segCount];
488 endCount = new char[segCount];
489 idDelta = new short[segCount];
490 idRangeOffset = new char[segCount];
491
492 for (int i=0; i<segCount; i++) {
493 endCount[i] = buffer.get();
494 }
495 buffer.get();
496 for (int i=0; i<segCount; i++) {
497 startCount[i] = buffer.get();
498 }
499
500 for (int i=0; i<segCount; i++) {
501 idDelta[i] = (short)buffer.get();
502 }
503
504 for (int i=0; i<segCount; i++) {
505 char ctmp = buffer.get();
506 idRangeOffset[i] = (char)((ctmp>>1)&0xffff);
507 }
508
509
510
511 int pos = (segCount*8+16)/2;
512 buffer.position(pos);
513 int numGlyphIds = (subtableLength/2 - pos);
514 glyphIds = new char[numGlyphIds];
515 for (int i=0;i<numGlyphIds;i++) {
516 glyphIds[i] = buffer.get();
517 }
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534 }
535
536 char getGlyph(int charCode) {
537
538 int index = 0;
539 char glyphCode = 0;
540
541 int controlGlyph = getControlCodeGlyph(charCode, true);
542 if (controlGlyph >= 0) {
543 return (char)controlGlyph;
544 }
545
546
547
548
549
550
551
552
553 if (xlat != null) {
554 charCode = xlat[charCode];
555 }
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575 int left = 0, right = startCount.length;
576 index = startCount.length >> 1;
577 while (left < right) {
578 if (endCount[index] < charCode) {
579 left = index + 1;
580 } else {
581 right = index;
582 }
583 index = (left + right) >> 1;
584 }
585
586 if (charCode >= startCount[index] && charCode <= endCount[index]) {
587 int rangeOffset = idRangeOffset[index];
588
589 if (rangeOffset == 0) {
590 glyphCode = (char)(charCode + idDelta[index]);
591 } else {
592
593
594
595
596
597
598
599
600
601 int glyphIDIndex = rangeOffset - segCount + index
602 + (charCode - startCount[index]);
603 glyphCode = glyphIds[glyphIDIndex];
604 if (glyphCode != 0) {
605 glyphCode = (char)(glyphCode + idDelta[index]);
606 }
607 }
608 }
609 if (glyphCode != 0) {
610
611 }
612 return glyphCode;
613 }
614 }
615
616
617 static class CMapFormat0 extends CMap {
618 byte [] cmap;
619
620 CMapFormat0(ByteBuffer buffer, int offset) {
621
622
623 int len = buffer.getChar(offset+2);
624 cmap = new byte[len-6];
625 buffer.position(offset+6);
626 buffer.get(cmap);
627 }
628
629 char getGlyph(int charCode) {
630 if (charCode < 256) {
631 if (charCode < 0x0010) {
632 switch (charCode) {
633 case 0x0009:
634 case 0x000a:
635 case 0x000d: return CharToGlyphMapper.INVISIBLE_GLYPH_ID;
636 }
637 }
638 return (char)(0xff & cmap[charCode]);
639 } else {
640 return 0;
641 }
642 }
643 }
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716 static class CMapFormat2 extends CMap {
717
718 char[] subHeaderKey = new char[256];
719
720
721
722
723
724
725
726
727 char[] firstCodeArray;
728 char[] entryCountArray;
729 short[] idDeltaArray;
730 char[] idRangeOffSetArray;
731
732 char[] glyphIndexArray;
733
734 CMapFormat2(ByteBuffer buffer, int offset, char[] xlat) {
735
736 this.xlat = xlat;
737
738 int tableLen = buffer.getChar(offset+2);
739 buffer.position(offset+6);
740 CharBuffer cBuffer = buffer.asCharBuffer();
741 char maxSubHeader = 0;
742 for (int i=0;i<256;i++) {
743 subHeaderKey[i] = cBuffer.get();
744 if (subHeaderKey[i] > maxSubHeader) {
745 maxSubHeader = subHeaderKey[i];
746 }
747 }
748
749
750
751
752 int numSubHeaders = (maxSubHeader >> 3) +1;
753 firstCodeArray = new char[numSubHeaders];
754 entryCountArray = new char[numSubHeaders];
755 idDeltaArray = new short[numSubHeaders];
756 idRangeOffSetArray = new char[numSubHeaders];
757 for (int i=0; i<numSubHeaders; i++) {
758 firstCodeArray[i] = cBuffer.get();
759 entryCountArray[i] = cBuffer.get();
760 idDeltaArray[i] = (short)cBuffer.get();
761 idRangeOffSetArray[i] = cBuffer.get();
762
763
764
765
766 }
767
768 int glyphIndexArrSize = (tableLen-518-numSubHeaders*8)/2;
769 glyphIndexArray = new char[glyphIndexArrSize];
770 for (int i=0; i<glyphIndexArrSize;i++) {
771 glyphIndexArray[i] = cBuffer.get();
772 }
773 }
774
775 char getGlyph(int charCode) {
776 int controlGlyph = getControlCodeGlyph(charCode, true);
777 if (controlGlyph >= 0) {
778 return (char)controlGlyph;
779 }
780
781 if (xlat != null) {
782 charCode = xlat[charCode];
783 }
784
785 char highByte = (char)(charCode >> 8);
786 char lowByte = (char)(charCode & 0xff);
787 int key = subHeaderKey[highByte]>>3;
788 char mapMe;
789
790 if (key != 0) {
791 mapMe = lowByte;
792 } else {
793 mapMe = highByte;
794 if (mapMe == 0) {
795 mapMe = lowByte;
796 }
797 }
798
799
800
801 char firstCode = firstCodeArray[key];
802 if (mapMe < firstCode) {
803 return 0;
804 } else {
805 mapMe -= firstCode;
806 }
807
808 if (mapMe < entryCountArray[key]) {
809
810
811
812
813
814
815
816
817
818
819
820
821
822 int glyphArrayOffset = ((idRangeOffSetArray.length-key)*8)-6;
823 int glyphSubArrayStart =
824 (idRangeOffSetArray[key] - glyphArrayOffset)/2;
825 char glyphCode = glyphIndexArray[glyphSubArrayStart+mapMe];
826 if (glyphCode != 0) {
827 glyphCode += idDeltaArray[key];
828 return glyphCode;
829 }
830 }
831 return 0;
832 }
833 }
834
835
836 static class CMapFormat6 extends CMap {
837
838 char firstCode;
839 char entryCount;
840 char[] glyphIdArray;
841
842 CMapFormat6(ByteBuffer bbuffer, int offset, char[] xlat) {
843
844 System.err.println("WARNING: CMapFormat8 is untested.");
845 bbuffer.position(offset+6);
846 CharBuffer buffer = bbuffer.asCharBuffer();
847 firstCode = buffer.get();
848 entryCount = buffer.get();
849 glyphIdArray = new char[entryCount];
850 for (int i=0; i< entryCount; i++) {
851 glyphIdArray[i] = buffer.get();
852 }
853 }
854
855 char getGlyph(int charCode) {
856 int controlGlyph = getControlCodeGlyph(charCode, true);
857 if (controlGlyph >= 0) {
858 return (char)controlGlyph;
859 }
860
861 if (xlat != null) {
862 charCode = xlat[charCode];
863 }
864
865 charCode -= firstCode;
866 if (charCode < 0 || charCode >= entryCount) {
867 return 0;
868 } else {
869 return glyphIdArray[charCode];
870 }
871 }
872 }
873
874
875
876
877
878 static class CMapFormat8 extends CMap {
879 byte[] is32 = new byte[8192];
880 int nGroups;
881 int[] startCharCode;
882 int[] endCharCode;
883 int[] startGlyphID;
884
885 CMapFormat8(ByteBuffer bbuffer, int offset, char[] xlat) {
886
887 System.err.println("WARNING: CMapFormat8 is untested.");
888 bbuffer.position(12);
889 bbuffer.get(is32);
890 nGroups = bbuffer.getInt();
891 startCharCode = new int[nGroups];
892 endCharCode = new int[nGroups];
893 startGlyphID = new int[nGroups];
894 }
895
896 char getGlyph(int charCode) {
897 if (xlat != null) {
898 throw new RuntimeException("xlat array for cmap fmt=8");
899 }
900 return 0;
901 }
902
903 }
904
905
906
907
908
909
910 static class CMapFormat10 extends CMap {
911
912 long firstCode;
913 int entryCount;
914 char[] glyphIdArray;
915
916 CMapFormat10(ByteBuffer bbuffer, int offset, char[] xlat) {
917
918 System.err.println("WARNING: CMapFormat10 is untested.");
919 firstCode = bbuffer.getInt() & INTMASK;
920 entryCount = bbuffer.getInt() & INTMASK;
921 bbuffer.position(offset+20);
922 CharBuffer buffer = bbuffer.asCharBuffer();
923 glyphIdArray = new char[entryCount];
924 for (int i=0; i< entryCount; i++) {
925 glyphIdArray[i] = buffer.get();
926 }
927 }
928
929 char getGlyph(int charCode) {
930
931 if (xlat != null) {
932 throw new RuntimeException("xlat array for cmap fmt=10");
933 }
934
935 int code = (int)(charCode - firstCode);
936 if (code < 0 || code >= entryCount) {
937 return 0;
938 } else {
939 return glyphIdArray[code];
940 }
941 }
942 }
943
944
945
946 static class CMapFormat12 extends CMap {
947
948 int numGroups;
949 int highBit =0;
950 int power;
951 int extra;
952 long[] startCharCode;
953 long[] endCharCode;
954 int[] startGlyphID;
955
956 CMapFormat12(ByteBuffer buffer, int offset, char[] xlat) {
957 if (xlat != null) {
958 throw new RuntimeException("xlat array for cmap fmt=12");
959 }
960
961 numGroups = buffer.getInt(offset+12);
962 startCharCode = new long[numGroups];
963 endCharCode = new long[numGroups];
964 startGlyphID = new int[numGroups];
965 buffer.position(offset+16);
966 buffer = buffer.slice();
967 IntBuffer ibuffer = buffer.asIntBuffer();
968 for (int i=0; i<numGroups; i++) {
969 startCharCode[i] = ibuffer.get() & INTMASK;
970 endCharCode[i] = ibuffer.get() & INTMASK;
971 startGlyphID[i] = ibuffer.get() & INTMASK;
972 }
973
974
975 int value = numGroups;
976
977 if (value >= 1 << 16) {
978 value >>= 16;
979 highBit += 16;
980 }
981
982 if (value >= 1 << 8) {
983 value >>= 8;
984 highBit += 8;
985 }
986
987 if (value >= 1 << 4) {
988 value >>= 4;
989 highBit += 4;
990 }
991
992 if (value >= 1 << 2) {
993 value >>= 2;
994 highBit += 2;
995 }
996
997 if (value >= 1 << 1) {
998 value >>= 1;
999 highBit += 1;
1000 }
1001
1002 power = 1 << highBit;
1003 extra = numGroups - power;
1004 }
1005
1006 char getGlyph(int charCode) {
1007 int controlGlyph = getControlCodeGlyph(charCode, false);
1008 if (controlGlyph >= 0) {
1009 return (char)controlGlyph;
1010 }
1011 int probe = power;
1012 int range = 0;
1013
1014 if (startCharCode[extra] <= charCode) {
1015 range = extra;
1016 }
1017
1018 while (probe > 1) {
1019 probe >>= 1;
1020
1021 if (startCharCode[range+probe] <= charCode) {
1022 range += probe;
1023 }
1024 }
1025
1026 if (startCharCode[range] <= charCode &&
1027 endCharCode[range] >= charCode) {
1028 return (char)
1029 (startGlyphID[range] + (charCode - startCharCode[range]));
1030 }
1031
1032 return 0;
1033 }
1034
1035 }
1036
1037
1038 static class NullCMapClass extends CMap {
1039
1040 char getGlyph(int charCode) {
1041 return 0;
1042 }
1043 }
1044
1045 public static final NullCMapClass theNullCmap = new NullCMapClass();
1046
1047 final int getControlCodeGlyph(int charCode, boolean noSurrogates) {
1048 if (charCode < 0x0010) {
1049 switch (charCode) {
1050 case 0x0009:
1051 case 0x000a:
1052 case 0x000d: return CharToGlyphMapper.INVISIBLE_GLYPH_ID;
1053 }
1054 } else if (charCode >= 0x200c) {
1055 if ((charCode <= 0x200f) ||
1056 (charCode >= 0x2028 && charCode <= 0x202e) ||
1057 (charCode >= 0x206a && charCode <= 0x206f)) {
1058 return CharToGlyphMapper.INVISIBLE_GLYPH_ID;
1059 } else if (noSurrogates && charCode >= 0xFFFF) {
1060 return 0;
1061 }
1062 }
1063 return -1;
1064 }
1065 }